home *** CD-ROM | disk | FTP | other *** search
/ SPACE 2 / SPACE - Library 2 - Volume 1.iso / program / 515 / rcs5ap1s.lzh / RCS.C < prev    next >
C/C++ Source or Header  |  1991-01-30  |  45KB  |  1,470 lines

  1. /*
  2.  *                      RCS create/change operation
  3.  */
  4. /* Copyright (C) 1982, 1988, 1989 Walter Tichy
  5.    Copyright 1990 by Paul Eggert
  6.    Distributed under license by the Free Software Foundation, Inc.
  7.  
  8. This file is part of RCS.
  9.  
  10. RCS is free software; you can redistribute it and/or modify
  11. it under the terms of the GNU General Public License as published by
  12. the Free Software Foundation; either version 1, or (at your option)
  13. any later version.
  14.  
  15. RCS is distributed in the hope that it will be useful,
  16. but WITHOUT ANY WARRANTY; without even the implied warranty of
  17. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  18. GNU General Public License for more details.
  19.  
  20. You should have received a copy of the GNU General Public License
  21. along with RCS; see the file COPYING.  If not, write to
  22. the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
  23.  
  24. Report problems and direct all questions to:
  25.  
  26.     rcs-bugs@cs.purdue.edu
  27.  
  28. */
  29.  
  30.  
  31.  
  32.  
  33. /* $Log: rcs.c,v $
  34.  * Revision 5.11  1991/01/30  14:21:32  apratt
  35.  * CI with RCS version 5
  36.  *
  37.  * Revision 5.10  91/01/30  12:02:40  apratt
  38.  * Changed RCS5AKP1 to RCS5AP1
  39.  * 
  40.  * Revision 5.9  91/01/29  17:45:30  apratt
  41.  * Added RCS5AKP1 to usage message
  42.  * 
  43.  * Revision 5.8  91/01/11  12:46:10  apratt
  44.  * First version that compiles.
  45.  * 
  46.  * Revision 5.7  90/12/18  17:19:21  eggert
  47.  * checked in with -k by apratt at 91.01.10.13.15.02.
  48.  * 
  49.  * Revision 5.7  1990/12/18  17:19:21  eggert
  50.  * Fix bug with multiple -n and -N options.
  51.  *
  52.  * Revision 5.6  1990/12/04  05:18:40  eggert
  53.  * Use -I for prompts and -q for diagnostics.
  54.  *
  55.  * Revision 5.5  1990/11/11  00:06:35  eggert
  56.  * Fix `rcs -e' core dump.
  57.  *
  58.  * Revision 5.4  1990/11/01  05:03:33  eggert
  59.  * Add -I and new -t behavior.  Permit arbitrary data in logs.
  60.  *
  61.  * Revision 5.3  1990/10/04  06:30:16  eggert
  62.  * Accumulate exit status across files.
  63.  *
  64.  * Revision 5.2  1990/09/04  08:02:17  eggert
  65.  * Standardize yes-or-no procedure.
  66.  *
  67.  * Revision 5.1  1990/08/29  07:13:51  eggert
  68.  * Remove unused setuid support.  Clean old log messages too.
  69.  *
  70.  * Revision 5.0  1990/08/22  08:12:42  eggert
  71.  * Don't lose names when applying -a option to multiple files.
  72.  * Remove compile-time limits; use malloc instead.  Add setuid support.
  73.  * Permit dates past 1999/12/31.  Make lock and temp files faster and safer.
  74.  * Ansify and Posixate.  Add -V.  Fix umask bug.  Make linting easier.  Tune.
  75.  * Yield proper exit status.  Check diff's output.
  76.  *
  77.  * Revision 4.11  89/05/01  15:12:06  narten
  78.  * changed copyright header to reflect current distribution rules
  79.  * 
  80.  * Revision 4.10  88/11/08  16:01:54  narten
  81.  * didn't install previous patch correctly
  82.  * 
  83.  * Revision 4.9  88/11/08  13:56:01  narten
  84.  * removed include <sysexits.h> (not needed)
  85.  * minor fix for -A option
  86.  * 
  87.  * Revision 4.8  88/08/09  19:12:27  eggert
  88.  * Don't access freed storage.
  89.  * Use execv(), not system(); yield proper exit status; remove lint.
  90.  * 
  91.  * Revision 4.7  87/12/18  11:37:17  narten
  92.  * lint cleanups (Guy Harris)
  93.  * 
  94.  * Revision 4.6  87/10/18  10:28:48  narten
  95.  * Updating verison numbers. Changes relative to 1.1 are actually 
  96.  * relative to 4.3
  97.  * 
  98.  * Revision 1.4  87/09/24  13:58:52  narten
  99.  * Sources now pass through lint (if you ignore printf/sprintf/fprintf 
  100.  * warnings)
  101.  * 
  102.  * Revision 1.3  87/03/27  14:21:55  jenkins
  103.  * Port to suns
  104.  * 
  105.  * Revision 1.2  85/12/17  13:59:09  albitz
  106.  * Changed setstate to rcs_setstate because of conflict with random.o.
  107.  * 
  108.  * Revision 4.3  83/12/15  12:27:33  wft
  109.  * rcs -u now breaks most recent lock if it can't find a lock by the caller.
  110.  * 
  111.  * Revision 4.2  83/12/05  10:18:20  wft
  112.  * Added conditional compilation for sending mail.
  113.  * Alternatives: V4_2BSD, V6, USG, and other.
  114.  * 
  115.  * Revision 4.1  83/05/10  16:43:02  wft
  116.  * Simplified breaklock(); added calls to findlock() and getcaller().
  117.  * Added option -b (default branch). Updated -s and -w for -b.
  118.  * Removed calls to stat(); now done by pairfilenames().
  119.  * Replaced most catchints() calls with restoreints().
  120.  * Removed check for exit status of delivermail().
  121.  * Directed all interactive output to stderr.
  122.  * 
  123.  * Revision 3.9.1.1  83/12/02  22:08:51  wft
  124.  * Added conditional compilation for 4.2 sendmail and 4.1 delivermail.
  125.  * 
  126.  * Revision 3.9  83/02/15  15:38:39  wft
  127.  * Added call to fastcopy() to copy remainder of RCS file.
  128.  *
  129.  * Revision 3.8  83/01/18  17:37:51  wft
  130.  * Changed sendmail(): now uses delivermail, and asks whether to break the lock.
  131.  *
  132.  * Revision 3.7  83/01/15  18:04:25  wft
  133.  * Removed putree(); replaced with puttree() in rcssyn.c.
  134.  * Combined putdellog() and scanlogtext(); deleted putdellog().
  135.  * Cleaned up diagnostics and error messages. Fixed problem with
  136.  * mutilated files in case of deletions in 2 files in a single command.
  137.  * Changed marking of selector from 'D' to DELETE.
  138.  *
  139.  * Revision 3.6  83/01/14  15:37:31  wft
  140.  * Added ignoring of interrupts while new RCS file is renamed;
  141.  * Avoids deletion of RCS files by interrupts.
  142.  *
  143.  * Revision 3.5  82/12/10  21:11:39  wft
  144.  * Removed unused variables, fixed checking of return code from diff,
  145.  * introduced variant COMPAT2 for skipping Suffix on -A files.
  146.  *
  147.  * Revision 3.4  82/12/04  13:18:20  wft
  148.  * Replaced getdelta() with gettree(), changed breaklock to update
  149.  * field lockedby, added some diagnostics.
  150.  *
  151.  * Revision 3.3  82/12/03  17:08:04  wft
  152.  * Replaced getlogin() with getpwuid(), flcose() with ffclose(),
  153.  * /usr/ucb/Mail with macro MAIL. Removed handling of Suffix (-x).
  154.  * fixed -u for missing revno. Disambiguated structure members.
  155.  *
  156.  * Revision 3.2  82/10/18  21:05:07  wft
  157.  * rcs -i now generates a file mode given by the umask minus write permission;
  158.  * otherwise, rcs keeps the mode, but removes write permission.
  159.  * I added a check for write error, fixed call to getlogin(), replaced
  160.  * curdir() with getfullRCSname(), cleaned up handling -U/L, and changed
  161.  * conflicting, long identifiers.
  162.  *
  163.  * Revision 3.1  82/10/13  16:11:07  wft
  164.  * fixed type of variables receiving from getc() (char -> int).
  165.  */
  166.  
  167.  
  168. #include "rcsbase.h"
  169.  
  170. struct  Lockrev {
  171.     const char *revno;
  172.         struct  Lockrev   * nextrev;
  173. };
  174.  
  175. struct  Symrev {
  176.     const char *revno;
  177.     const char *ssymbol;
  178.         int     override;
  179.         struct  Symrev  * nextsym;
  180. };
  181.  
  182. struct  Status {
  183.     const char *revno;
  184.     const char *status;
  185.         struct  Status  * nextstatus;
  186. };
  187.  
  188. enum changeaccess {append, erase};
  189. struct chaccess {
  190.     const char *login;
  191.     enum changeaccess command;
  192.     struct chaccess *nextchaccess;
  193. };
  194.  
  195. struct delrevpair {
  196.     const char *strt;
  197.     const char *end;
  198.         int     code;
  199. };
  200.  
  201. static int buildeltatext P((const struct hshentries*));
  202. static int removerevs P((void));
  203. static int sendmail P((const char*,const char*));
  204. static struct Lockrev *rmnewlocklst P((const struct Lockrev*));
  205. static void breaklock P((const struct hshentry*));
  206. static void buildtree P((void));
  207. static void cleanup P((void));
  208. static void getaccessor P((char*,enum changeaccess));
  209. static void getassoclst P((int,char*));
  210. static void getchaccess P((const char*,enum changeaccess));
  211. static void getdelrev P((char*));
  212. static void getstates P((char*));
  213. static void rcs_setstate P((const char*,const char*));
  214. static void scanlogtext P((struct hshentry*,int));
  215. static void setlock P((const char*));
  216. static void updateaccess P((void));
  217. static void updateassoc P((void));
  218. static void updatelocks P((void));
  219.  
  220. static struct buf numrev;
  221. static const char *headstate;
  222. static int chgheadstate, exitstatus, lockhead, unlockcaller;
  223. static struct Lockrev *newlocklst, *rmvlocklst;
  224. static struct Status *statelst, *laststate;
  225. static struct Symrev *assoclst, *lastassoc;
  226. static struct chaccess *chaccess, **nextchaccess;
  227. static struct delrevpair delrev;
  228. static struct hshentry *cuthead, *cuttail, *delstrt;
  229. static struct hshentries *gendeltas;
  230.  
  231. mainProg(rcsId, "rcs", "$Id: rcs.c,v 5.11 1991/01/30 14:21:32 apratt Exp $")
  232. {
  233.     static const char cmdusage[] =
  234.         "\nRCS5AP1 as modified for TOS by Allan Pratt, atari!apratt\nrcs usage: rcs -alogins -Aoldfile -{blu}[rev] -cstring -e[logins] -i -{LU} -{nN}name[:rev] -orange -sstate[:rev] -t[textfile] -Vn file ...";
  235.  
  236.     const char *branchsym, *commsyml, *textfile;
  237.     int branchflag, expmode, initflag;
  238.     int r, strictlock, strict_selected, textflag;
  239.     mode_t defaultRCSmode;    /* default mode for new RCS files */
  240.     struct buf branchnum;
  241.         struct  Lockrev *curlock,  * rmvlock, *lockpt;
  242.         struct  Status  * curstate;
  243.  
  244.     initid();
  245.     catchints();
  246.  
  247.     nextchaccess = &chaccess;
  248.     branchsym = commsyml = textfile = nil;
  249.     branchflag = strictlock = false;
  250.     bufautobegin(&branchnum);
  251.     curlock = rmvlock = nil;
  252.     defaultRCSmode = 0;
  253.     expmode = -1;
  254.         initflag= textflag = false;
  255.         strict_selected = 0;
  256.  
  257.         /*  preprocessing command options    */
  258.         while (--argc,++argv, argc>=1 && ((*argv)[0] == '-')) {
  259.                 switch ((*argv)[1]) {
  260.  
  261.         case 'i':   /*  initial version  */
  262.                         initflag = true;
  263.                         break;
  264.  
  265.                 case 'b':  /* change default branch */
  266.             if (branchflag) redefined('b');
  267.                         branchflag= true;
  268.                         branchsym = (*argv)+2;
  269.                         break;
  270.  
  271.                 case 'c':   /*  change comment symbol   */
  272.             if (commsyml) redefined('c');
  273.                         commsyml = (*argv)+2;
  274.                         break;
  275.  
  276.                 case 'a':  /*  add new accessor   */
  277.             getaccessor(*argv+1, append);
  278.                         break;
  279.  
  280.                 case 'A':  /*  append access list according to accessfile  */
  281.             *argv += 2;
  282.             if (!**argv) {
  283.                 error("missing file name after -A");
  284.                             break;
  285.                         }
  286.             if (0 < pairfilenames(1,argv,rcsreadopen,true,false)) {
  287.                 while (AccessList) {
  288.                 getchaccess(strsave(AccessList->login), append);
  289.                 AccessList = AccessList->nextaccess;
  290.                 }
  291.                 ffclose(finptr);
  292.                         }
  293.                         break;
  294.  
  295.                 case 'e':    /*  remove accessors   */
  296.             getaccessor(*argv+1, erase);
  297.                         break;
  298.  
  299.                 case 'l':    /*   lock a revision if it is unlocked   */
  300.                         if ( (*argv)[2] == '\0'){ /* lock head or def. branch */
  301.                             lockhead = true;
  302.                             break;
  303.                         }
  304.             lockpt = talloc(struct Lockrev);
  305.                         lockpt->revno = (*argv)+2;
  306.                         lockpt->nextrev = nil;
  307.                         if ( curlock )
  308.                             curlock->nextrev = lockpt;
  309.                         else
  310.                             newlocklst = lockpt;
  311.                         curlock = lockpt;
  312.                         break;
  313.  
  314.                 case 'u':   /*  release lock of a locked revision   */
  315.                         if ( (*argv)[2] == '\0'){ /*  unlock head  */
  316.                             unlockcaller=true;
  317.                             break;
  318.                         }
  319.             lockpt = talloc(struct Lockrev);
  320.                         lockpt->revno = (*argv)+2;
  321.                         lockpt->nextrev = nil;
  322.                         if (rmvlock)
  323.                             rmvlock->nextrev = lockpt;
  324.                         else
  325.                             rmvlocklst = lockpt;
  326.                         rmvlock = lockpt;
  327.  
  328.                         curlock = rmnewlocklst(lockpt);
  329.                         break;
  330.  
  331.                 case 'L':   /*  set strict locking */
  332.                         if (strict_selected++) {  /* Already selected L or U? */
  333.                if (!strictlock)      /* Already selected -U? */
  334.                    warn("-L overrides -U.");
  335.                         }
  336.                         strictlock = true;
  337.                         break;
  338.  
  339.                 case 'U':   /*  release strict locking */
  340.                         if (strict_selected++) {  /* Already selected L or U? */
  341.                if (strictlock)      /* Already selected -L? */
  342.                    warn("-L overrides -U.");
  343.                         }
  344.             else
  345.                 strictlock = false;
  346.                         break;
  347.  
  348.                 case 'n':    /*  add new association: error, if name exists */
  349.                         if ( (*argv)[2] == '\0') {
  350.                 error("missing symbolic name after -n");
  351.                             break;
  352.                         }
  353.                         getassoclst(false, (*argv)+1);
  354.                         break;
  355.  
  356.                 case 'N':   /*  add or change association   */
  357.                         if ( (*argv)[2] == '\0') {
  358.                 error("missing symbolic name after -N");
  359.                             break;
  360.                         }
  361.                         getassoclst(true, (*argv)+1);
  362.                         break;
  363.  
  364.         case 'o':   /*  delete revisions  */
  365.             if (delrev.strt) redefined('o');
  366.                         if ( (*argv)[2] == '\0' ) {
  367.                 error("missing revision range after -o");
  368.                             break;
  369.                         }
  370.                         getdelrev( (*argv)+1 );
  371.                         break;
  372.  
  373.                 case 's':   /*  change state attribute of a revision  */
  374.                         if ( (*argv)[2] == '\0') {
  375.                 error("state missing after -s");
  376.                             break;
  377.                         }
  378.                         getstates( (*argv)+1);
  379.                         break;
  380.  
  381.                 case 't':   /*  change descriptive text   */
  382.                         textflag=true;
  383.                         if ((*argv)[2]!='\0'){
  384.                 if (textfile) redefined('t');
  385.                                 textfile = (*argv)+2;
  386.                         }
  387.                         break;
  388.  
  389.         case 'I':
  390.             interactiveflag = true;
  391.             break;
  392.  
  393.                 case 'q':
  394.                         quietflag = true;
  395.                         break;
  396.  
  397.         case 'V':
  398.             setRCSversion(*argv);
  399.             break;
  400.  
  401.         case 'k':    /*  set keyword expand mode  */
  402.             if (0 <= expmode) redefined('k');
  403.             if (0 <= (expmode = str2expmode(*argv+2)))
  404.                 break;
  405.             /* fall into */
  406.                 default:
  407.             faterror("unknown option: %s%s", *argv, cmdusage);
  408.                 };
  409.         }  /* end processing of options */
  410.  
  411.     if (argc<1) faterror("no input file%s", cmdusage);
  412.         if (nerror) {
  413.         diagnose("%s aborted\n",cmdid);
  414.         exitmain(EXIT_FAILURE);
  415.         }
  416.     if (initflag) {
  417.         defaultRCSmode = umask((mode_t)0);
  418.         VOID umask(defaultRCSmode);
  419.         defaultRCSmode = ~defaultRCSmode & 0444;
  420.     }
  421.  
  422.         /* now handle all filenames */
  423.         do {
  424.     foutptr = NULL;
  425.         finptr=frewrite=NULL;
  426.     ffree();
  427.  
  428.         if ( initflag ) {
  429.         switch (pairfilenames(argc, argv, rcswriteopen, false, false)) {
  430.                 case -1: break;        /*  not exist; ok */
  431.                 case  0: continue;     /*  error         */
  432.                 case  1: error("file %s exists already", RCSfilename);
  433.                          continue;
  434.             }
  435.     }
  436.         else  {
  437.         switch (pairfilenames(argc, argv, rcswriteopen, true, false)) {
  438.                 case -1: continue;    /*  not exist      */
  439.                 case  0: continue;    /*  errors         */
  440.                 case  1: break;       /*  file exists; ok*/
  441.             }
  442.     }
  443.  
  444.  
  445.         /* now RCSfilename contains the name of the RCS file, and
  446.          * workfilename contains the name of the working file.
  447.          * if !initflag, finptr contains the file descriptor for the
  448.          * RCS file. The admin node is initialized.
  449.          */
  450.  
  451.     diagnose("RCS file: %s\n", RCSfilename);
  452.  
  453.     if (initflag && !getworkstat())           continue; /* give up */
  454.     if (!initflag && !checkaccesslist())       continue; /* give up */
  455.  
  456.         gettree(); /* read in delta tree */
  457.  
  458.         /*  update admin. node    */
  459.         if (strict_selected) StrictLocks = strictlock;
  460.     if (commsyml) {
  461.         Comment.string = commsyml;
  462.         Comment.size = strlen(commsyml);
  463.     }
  464.     if (0 <= expmode) Expand = expmode;
  465.  
  466.         /* update default branch */
  467.     if (branchflag && expandsym(branchsym, &branchnum)) {
  468.         if (countnumflds(branchnum.string)) {
  469.         Dbranch = branchnum.string;
  470.             } else
  471.                 Dbranch = nil;
  472.         }
  473.  
  474.     updateaccess();        /*  update access list        */
  475.  
  476.         updateassoc();          /*  update association list   */
  477.  
  478.         updatelocks();          /*  update locks              */
  479.  
  480.         /*  update state attribution  */
  481.         if (chgheadstate) {
  482.             /* change state of default branch or head */
  483.             if (Dbranch==nil) {
  484.                 if (Head==nil)
  485.              warn("can't change states in an empty tree");
  486.                 else Head->state = headstate;
  487.             } else {
  488.         rcs_setstate(Dbranch,headstate); /* Can't set directly */
  489.             }
  490.         }
  491.         curstate = statelst;
  492.         while( curstate ) {
  493.             rcs_setstate(curstate->revno,curstate->status);
  494.             curstate = curstate->nextstatus;
  495.         }
  496.  
  497.         cuthead = cuttail = nil;
  498.     if (delrev.strt && removerevs()) {
  499.             /*  rebuild delta tree if some deltas are deleted   */
  500.             if ( cuttail )
  501.         VOID genrevs(cuttail->num, (char *)nil,(char *)nil,
  502.                  (char *)nil, &gendeltas);
  503.             buildtree();
  504.         }
  505.  
  506.  
  507.         putadmin(frewrite);
  508.         if ( Head )
  509.            puttree(Head, frewrite);
  510.     putdesc(textflag,textfile);
  511.     foutptr = NULL;
  512.  
  513.         if ( Head) {
  514.         if (!delrev.strt) {
  515.                 /* no revision deleted */
  516.                 fastcopy(finptr,frewrite);
  517.             } else {
  518.         if (!cuttail || buildeltatext(gendeltas))
  519.                     scanlogtext((struct hshentry *)nil,nil);
  520.                     /* copy rest of delta text nodes that are not deleted      */
  521.             }
  522.         }
  523.     if (finptr) {ffclose(finptr); finptr=NULL;} /* Help the file system. */
  524.         ffclose(frewrite);   frewrite = NULL;
  525.         if ( ! nerror ) {  /*  move temporary file to RCS file if no error */
  526.         /* update mode */
  527.         seteid();
  528.         r = chmod(newRCSfilename,
  529.              (
  530.                    !initflag ? RCSstat.st_mode
  531.                  : haveworkstat==0 ? workstat.st_mode
  532.                  : defaultRCSmode
  533.              ) & ~(S_IWUSR|S_IWGRP|S_IWOTH)
  534.         );
  535.         if (r == 0) {
  536.         ignoreints();
  537.         r = re_name(newRCSfilename,RCSfilename);
  538.         keepdirtemp(newRCSfilename);
  539.         restoreints();
  540.         }
  541.         setrid();
  542.         if (r != 0) {
  543.         eerror(RCSfilename);
  544.         error("saved in %s", newRCSfilename);
  545.         dirtempunlink();
  546.                 break;
  547.             }
  548.         diagnose("done\n");
  549.         } else {
  550.         diagnose("%s aborted; %s unchanged.\n",cmdid,RCSfilename);
  551.         }
  552.     } while (cleanup(),
  553.                  ++argv, --argc >=1);
  554.  
  555.     tempunlink();
  556.     exitmain(exitstatus);
  557. }       /* end of main (rcs) */
  558.  
  559.     static void
  560. cleanup()
  561. {
  562.     if (nerror) exitstatus = EXIT_FAILURE;
  563.     if (finptr) ffclose(finptr);
  564.     if (frewrite) ffclose(frewrite);
  565.     dirtempunlink();
  566. }
  567.  
  568.     exiting void
  569. exiterr()
  570. {
  571.     dirtempunlink();
  572.     tempunlink();
  573.     _exit(EXIT_FAILURE);
  574. }
  575.  
  576.  
  577.     static void
  578. getassoclst(flag, sp)
  579. int     flag;
  580. char    * sp;
  581. /*  Function:   associate a symbolic name to a revision or branch,      */
  582. /*              and store in assoclst                                   */
  583.  
  584. {
  585.         struct   Symrev  * pt;
  586.     const char *temp;
  587.         int                c;
  588.  
  589.         while( (c=(*++sp)) == ' ' || c == '\t' || c =='\n')  ;
  590.         temp = sp;
  591.     sp = checkid(sp, ':');  /*  check for invalid symbolic name  */
  592.     c = *sp;   *sp = '\0';
  593.         while( c == ' ' || c == '\t' || c == '\n')  c = *++sp;
  594.  
  595.         if ( c != ':' && c != '\0') {
  596.         error("invalid string %s after option -n or -N",sp);
  597.             return;
  598.         }
  599.  
  600.     pt = talloc(struct Symrev);
  601.         pt->ssymbol = temp;
  602.         pt->override = flag;
  603.     if (c == '\0')  /*  delete symbol  */
  604.             pt->revno = nil;
  605.         else {
  606.             while( (c = *++sp) == ' ' || c == '\n' || c == '\t')  ;
  607.         if ( c == '\0' )
  608.                 pt->revno = nil;
  609.         else
  610.                 pt->revno = sp;
  611.         }
  612.         pt->nextsym = nil;
  613.         if (lastassoc)
  614.             lastassoc->nextsym = pt;
  615.         else
  616.             assoclst = pt;
  617.         lastassoc = pt;
  618.         return;
  619. }
  620.  
  621.  
  622.     static void
  623. getchaccess(login, command)
  624.     const char *login;
  625.     enum changeaccess command;
  626. {
  627.     register struct chaccess *pt;
  628.  
  629.     *nextchaccess = pt = talloc(struct chaccess);
  630.     pt->login = login;
  631.     pt->command = command;
  632.     pt->nextchaccess = nil;
  633.     nextchaccess = &pt->nextchaccess;
  634. }
  635.  
  636.  
  637.  
  638.     static void
  639. getaccessor(opt, command)
  640.     char *opt;
  641.     enum changeaccess command;
  642. /*   Function:  get the accessor list of options -e and -a,     */
  643. /*        and store in chaccess                */
  644.  
  645.  
  646. {
  647.         register c;
  648.     register char *sp;
  649.  
  650.     sp = opt;
  651.         while( ( c = *++sp) == ' ' || c == '\n' || c == '\t' || c == ',') ;
  652.         if ( c == '\0') {
  653.         if (command == erase  &&  sp-opt == 1) {
  654.         getchaccess((const char*)nil, command);
  655.         return;
  656.         }
  657.         error("missing login name after option -a or -e");
  658.         return;
  659.         }
  660.  
  661.         while( c != '\0') {
  662.         getchaccess(sp, command);
  663.         sp = checkid(sp,',');
  664.         c = *sp;   *sp = '\0';
  665.                 while( c == ' ' || c == '\n' || c == '\t'|| c == ',')c =(*++sp);
  666.         }
  667. }
  668.  
  669.  
  670.  
  671.     static void
  672. getstates(sp)
  673. char    *sp;
  674. /*   Function:  get one state attribute and the corresponding   */
  675. /*              revision and store in statelst                  */
  676.  
  677. {
  678.     const char *temp;
  679.         struct  Status  *pt;
  680.         register        c;
  681.  
  682.         while( (c=(*++sp)) ==' ' || c == '\t' || c == '\n')  ;
  683.         temp = sp;
  684.     sp = checkid(sp,':');  /* check for invalid state attribute */
  685.     c = *sp;   *sp = '\0';
  686.         while( c == ' ' || c == '\t' || c == '\n' )  c = *++sp;
  687.  
  688.         if ( c == '\0' ) {  /*  change state of def. branch or Head  */
  689.             chgheadstate = true;
  690.             headstate  = temp;
  691.             return;
  692.         }
  693.         else if ( c != ':' ) {
  694.         error("missing ':' after state in option -s");
  695.             return;
  696.         }
  697.  
  698.         while( (c = *++sp) == ' ' || c == '\t' || c == '\n')  ;
  699.     pt = talloc(struct Status);
  700.         pt->status     = temp;
  701.         pt->revno      = sp;
  702.         pt->nextstatus = nil;
  703.         if (laststate)
  704.             laststate->nextstatus = pt;
  705.         else
  706.             statelst = pt;
  707.         laststate = pt;
  708. }
  709.  
  710.  
  711.  
  712.     static void
  713. getdelrev(sp)
  714. char    *sp;
  715. /*   Function:  get revision range or branch to be deleted,     */
  716. /*              and place in delrev                             */
  717. {
  718.         int    c;
  719.         struct  delrevpair      *pt;
  720.  
  721.     pt = &delrev;
  722.         while((c = (*++sp)) == ' ' || c == '\n' || c == '\t') ;
  723.  
  724.         if ( c == '<' || c == '-' ) {  /*  -o  -rev  or <rev  */
  725.             while( (c = (*++sp)) == ' ' || c == '\n' || c == '\t')  ;
  726.             pt->strt = sp;    pt->code = 1;
  727.             while( c != ' ' && c != '\n' && c != '\t' && c != '\0') c =(*++sp);
  728.             *sp = '\0';
  729.         pt->end = nil;
  730.             return;
  731.         }
  732.         else {
  733.             pt->strt = sp;
  734.             while( c != ' ' && c != '\n' && c != '\t' && c != '\0'
  735.                    && c != '-' && c != '<' )  c = *++sp;
  736.             *sp = '\0';
  737.             while( c == ' ' || c == '\n' || c == '\t' )  c = *++sp;
  738.             if ( c == '\0' )  {  /*   -o rev or branch   */
  739.                 pt->end = nil;   pt->code = 0;
  740.                 return;
  741.             }
  742.             if ( c != '-' && c != '<') {
  743.         faterror("invalid range %s %s after -o", pt->strt, sp);
  744.             }
  745.             while( (c = *++sp) == ' ' || c == '\n' || c == '\t')  ;
  746.             if ( c == '\0') {  /*  -o   rev-   or   rev<   */
  747.                 pt->end = nil;   pt->code = 2;
  748.                 return;
  749.             }
  750.         }
  751.         /*   -o   rev1-rev2    or   rev1<rev2   */
  752.     pt->end = sp;  pt->code = 3;
  753.         while( c!= ' ' && c != '\n' && c != '\t' && c != '\0') c = *++sp;
  754.         *sp = '\0';
  755. }
  756.  
  757.  
  758.  
  759.  
  760.     static void
  761. scanlogtext(delta,edit)
  762.     struct hshentry *delta;
  763.     int edit;
  764. /* Function: Scans delta text nodes up to and including the one given
  765.  * by delta, or up to last one present, if delta==nil.
  766.  * For the one given by delta (if delta!=nil), the log message is saved into
  767.  * curlogmsg and the text is edited if 'edit' is set, copied otherwise.
  768.  * Assumes the initial lexeme must be read in first.
  769.  * Does not advance nexttok after it is finished, except if delta==nil.
  770.  */
  771. {
  772.     const struct hshentry *nextdelta;
  773.     struct cbuf cb;
  774.  
  775.     for (;;) {
  776.         foutptr = NULL;
  777.                 nextlex();
  778.                 if (!(nextdelta=getnum())) {
  779.                     if(delta)
  780.             faterror("can't find delta for revision %s", delta->num);
  781.             if (nexttok != EOFILE)
  782.             fatserror("expecting EOF");
  783.             return; /* no more delta text nodes */
  784.                 }
  785.         if (nextdelta->selector) {
  786.             foutptr = frewrite;
  787.             aprintf(frewrite,DELNUMFORM,nextdelta->num,Klog);
  788.                 }
  789.         getkeystring(Klog);
  790.         if (delta==nextdelta) {
  791.             cb = savestring(&curlogbuf);
  792.             delta->log = curlogmsg =
  793.                 cleanlogmsg(curlogbuf.string, cb.size);
  794.                 } else {readstring();
  795.                 }
  796.                 nextlex();
  797.         while (nexttok==ID && strcmp(NextString,Ktext)!=0)
  798.             ignorephrase();
  799.         getkeystring(Ktext);
  800.  
  801.         if (delta==nextdelta)
  802.             break;
  803.         readstring(); /* skip over it */
  804.  
  805.     }
  806.     /* got the one we're looking for */
  807.     if (edit)
  808.         editstring((struct hshentry *)nil);
  809.     else
  810.         copystring();
  811. }
  812.  
  813.  
  814.  
  815.     static struct Lockrev *
  816. rmnewlocklst(which)
  817.     const struct Lockrev *which;
  818. /*   Function:  remove lock to revision which->revno from newlocklst   */
  819.  
  820. {
  821.         struct  Lockrev   * pt, *pre;
  822.  
  823.         while( newlocklst && (! strcmp(newlocklst->revno, which->revno))){
  824.         struct Lockrev *pn = newlocklst->nextrev;
  825.         tfree(newlocklst);
  826.         newlocklst = pn;
  827.         }
  828.  
  829.         pt = pre = newlocklst;
  830.         while( pt ) {
  831.             if ( ! strcmp(pt->revno, which->revno) ) {
  832.                 pre->nextrev = pt->nextrev;
  833.         tfree(pt);
  834.         pt = pre->nextrev;
  835.             }
  836.             else {
  837.                 pre = pt;
  838.                 pt = pt->nextrev;
  839.             }
  840.         }
  841.         return pre;
  842. }
  843.  
  844.  
  845.  
  846.     static void
  847. updateaccess()
  848. {
  849.     register struct chaccess *ch;
  850.     register struct access **p, *t;
  851.  
  852.     for (ch = chaccess;  ch;  ch = ch->nextchaccess) {
  853.         switch (ch->command) {
  854.         case erase:
  855.             if (!ch->login)
  856.                 AccessList = nil;
  857.             else
  858.                 for (p = &AccessList;  (t = *p);  )
  859.                 if (strcmp(ch->login, t->login) == 0)
  860.                     *p = t->nextaccess;
  861.                 else
  862.                     p = &t->nextaccess;
  863.             break;
  864.         case append:
  865.             for (p = &AccessList;  ;  p = &t->nextaccess)
  866.                 if (!(t = *p)) {
  867.                     *p = t = ftalloc(struct access);
  868.                     t->login = ch->login;
  869.                     t->nextaccess = nil;
  870.                     break;
  871.                 } else if (strcmp(ch->login, t->login) == 0)
  872.                     break;
  873.             break;
  874.         }
  875.     }
  876. }
  877.  
  878.  
  879.     static int
  880. sendmail(Delta, who)
  881.     const char *Delta, *who;
  882. /*   Function:  mail to who, informing him that his lock on delta was
  883.  *   broken by caller. Ask first whether to go ahead. Return false on
  884.  *   error or if user decides not to break the lock.
  885.  */
  886. {
  887. #if !DONT_SEND_MAIL
  888.     const char *messagefile;
  889.     int old1, old2, c;
  890.         FILE    * mailmess;
  891. #endif
  892.  
  893.     aprintf(stderr, "Revision %s is already locked by %s.\n", Delta, who);
  894.     if (!yesorno(false, "Do you want to break the lock? [ny](n): "))
  895.         return false;
  896.  
  897.         /* go ahead with breaking  */
  898.  
  899. #if !DONT_SEND_MAIL
  900.     messagefile = maketemp(0);
  901.     errno = 0;
  902.         if ( (mailmess = fopen(messagefile, "w")) == NULL) {
  903.         efaterror(messagefile);
  904.         }
  905.  
  906.     aprintf(mailmess, "Subject: Broken lock on %s\n\nYour lock on revision %s of file %s\nhas been broken by %s for the following reason:\n",
  907.         bindex(RCSfilename,SLASH), Delta, getfullRCSname(), getcaller()
  908.     );
  909.     aputs("State the reason for breaking the lock:\n(terminate with single '.' or end of file)\n>> ", stderr);
  910.  
  911.         old1 = '\n';    old2 = ' ';
  912.         for (; ;) {
  913.         c = getcstdin();
  914.             if ( c == EOF ) {
  915.         aprintf(mailmess, "%c\n", old1);
  916.                 break;
  917.             }
  918.             else if ( c == '\n' && old1 == '.' && old2 == '\n')
  919.                 break;
  920.             else {
  921.         afputc(old1, mailmess);
  922.                 old2 = old1;   old1 = c;
  923.         if (c=='\n') aputs(">> ", stderr);
  924.             }
  925.         }
  926.         ffclose(mailmess);
  927.  
  928.     /* ignore the exit status, even if delivermail unsuccessful */
  929.     VOID run(messagefile, (char*)nil, SENDMAIL, who, (char*)nil);
  930.  
  931. #else
  932.     /* this is the DONT_SEND_MAIL case */
  933.     aprintf(stderr,"Please tell %s that you broke the lock and why.",who);
  934. #endif
  935.     return(true);
  936. }
  937.  
  938.  
  939.  
  940.     static void
  941. breaklock(delta)
  942.     const struct hshentry *delta;
  943. /* function: Finds the lock held by caller on delta,
  944.  * and removes it.
  945.  * Sends mail if a lock different from the caller's is broken.
  946.  * Prints an error message if there is no such lock or error.
  947.  */
  948. {
  949.         register struct lock * next, * trail;
  950.     const char *num;
  951.         struct lock dummy;
  952.  
  953.     num=delta->num;
  954.         dummy.nextlock=next=Locks;
  955.         trail = &dummy;
  956.         while (next!=nil) {
  957.         if (strcmp(num, next->delta->num) == 0) {
  958.             if (
  959.                 strcmp(getcaller(),next->login) != 0
  960.                 &&    !sendmail(num, next->login)
  961.             ) {
  962.                 error("%s still locked by %s", num, next->login);
  963.                 return;
  964.             }
  965.             break; /* exact match */
  966.                 }
  967.                 trail=next;
  968.                 next=next->nextlock;
  969.         }
  970.         if (next!=nil) {
  971.                 /*found one */
  972.         diagnose("%s unlocked\n",next->delta->num);
  973.                 trail->nextlock=next->nextlock;
  974.                 next->delta->lockedby=nil;
  975.                 Locks=dummy.nextlock;
  976.         } else  {
  977.         error("no lock set on revision %s", num);
  978.         }
  979. }
  980.  
  981.  
  982.  
  983.     static struct hshentry *
  984. searchcutpt(object, length, store)
  985.     const char *object;
  986.     unsigned length;
  987.     struct hshentries *store;
  988. /*   Function:  Search store and return entry with number being object. */
  989. /*              cuttail = nil, if the entry is Head; otherwise, cuttail */
  990. /*              is the entry point to the one with number being object  */
  991.  
  992. {
  993.     cuthead = nil;
  994.     while (compartial(store->first->num, object, length)) {
  995.         cuthead = store->first;
  996.         store = store->rest;
  997.     }
  998.     return store->first;
  999. }
  1000.  
  1001.  
  1002.  
  1003.     static int
  1004. branchpoint(strt, tail)
  1005. struct  hshentry        *strt,  *tail;
  1006. /*   Function: check whether the deltas between strt and tail    */
  1007. /*        are locked or branch point, return 1 if any is  */
  1008. /*        locked or branch point; otherwise, return 0 and */
  1009. /*        mark deleted                    */
  1010.  
  1011. {
  1012.         struct  hshentry    *pt;
  1013.     const struct lock *lockpt;
  1014.         int     flag;
  1015.  
  1016.  
  1017.         pt = strt;
  1018.         flag = false;
  1019.         while( pt != tail) {
  1020.             if ( pt->branches ){ /*  a branch point  */
  1021.                 flag = true;
  1022.         error("can't remove branch point %s", pt->num);
  1023.             }
  1024.         lockpt = Locks;
  1025.         while(lockpt && lockpt->delta != pt)
  1026.         lockpt = lockpt->nextlock;
  1027.         if ( lockpt ) {
  1028.         flag = true;
  1029.         error("can't remove locked revision %s",pt->num);
  1030.         }
  1031.             pt = pt->next;
  1032.         }
  1033.  
  1034.         if ( ! flag ) {
  1035.             pt = strt;
  1036.             while( pt != tail ) {
  1037.         pt->selector = false;
  1038.         diagnose("deleting revision %s\n",pt->num);
  1039.                 pt = pt->next;
  1040.             }
  1041.         }
  1042.         return flag;
  1043. }
  1044.  
  1045.  
  1046.  
  1047.     static int
  1048. removerevs()
  1049. /*   Function:  get the revision range to be removed, and place the     */
  1050. /*              first revision removed in delstrt, the revision before  */
  1051. /*              delstrt in cuthead( nil, if delstrt is head), and the   */
  1052. /*              revision after the last removed revision in cuttail(nil */
  1053. /*              if the last is a leaf                                   */
  1054.  
  1055. {
  1056.     struct  hshentry *target, *target2, *temp;
  1057.     unsigned length;
  1058.     int flag;
  1059.  
  1060.         flag = false;
  1061.     if (!expandsym(delrev.strt, &numrev)) return 0;
  1062.     target = genrevs(numrev.string, (char*)nil, (char*)nil, (char*)nil, &gendeltas);
  1063.         if ( ! target ) return 0;
  1064.     if (cmpnum(target->num, numrev.string)) flag = true;
  1065.     length = countnumflds(numrev.string);
  1066.  
  1067.     if (delrev.code == 0) {  /*  -o  rev    or    -o  branch   */
  1068.         if (length & 1)
  1069.         temp=searchcutpt(target->num,length+1,gendeltas);
  1070.         else if (flag) {
  1071.         error("Revision %s doesn't exist.", numrev.string);
  1072.         return 0;
  1073.         }
  1074.         else
  1075.         temp = searchcutpt(numrev.string, length, gendeltas);
  1076.         cuttail = target->next;
  1077.             if ( branchpoint(temp, cuttail) ) {
  1078.                 cuttail = nil;
  1079.                 return 0;
  1080.             }
  1081.             delstrt = temp;     /* first revision to be removed   */
  1082.             return 1;
  1083.         }
  1084.  
  1085.     if (length & 1) {   /*  invalid branch after -o   */
  1086.         error("invalid branch range %s after -o", numrev.string);
  1087.             return 0;
  1088.         }
  1089.  
  1090.     if (delrev.code == 1) {  /*  -o  -rev   */
  1091.             if ( length > 2 ) {
  1092.                 temp = searchcutpt( target->num, length-1, gendeltas);
  1093.                 cuttail = target->next;
  1094.             }
  1095.             else {
  1096.                 temp = searchcutpt(target->num, length, gendeltas);
  1097.                 cuttail = target;
  1098.                 while( cuttail && ! cmpnumfld(target->num,cuttail->num,1) )
  1099.                     cuttail = cuttail->next;
  1100.             }
  1101.             if ( branchpoint(temp, cuttail) ){
  1102.                 cuttail = nil;
  1103.                 return 0;
  1104.             }
  1105.             delstrt = temp;
  1106.             return 1;
  1107.         }
  1108.  
  1109.     if (delrev.code == 2) {   /*  -o  rev-   */
  1110.             if ( length == 2 ) {
  1111.                 temp = searchcutpt(target->num, 1,gendeltas);
  1112.                 if ( flag)
  1113.                     cuttail = target;
  1114.                 else
  1115.                     cuttail = target->next;
  1116.             }
  1117.             else  {
  1118.                 if ( flag){
  1119.                     cuthead = target;
  1120.                     if ( !(temp = target->next) ) return 0;
  1121.                 }
  1122.                 else
  1123.                     temp = searchcutpt(target->num, length, gendeltas);
  1124.         getbranchno(temp->num, &numrev);  /* get branch number */
  1125.         target = genrevs(numrev.string, (char*)nil, (char*)nil, (char*)nil, &gendeltas);
  1126.             }
  1127.             if ( branchpoint( temp, cuttail ) ) {
  1128.                 cuttail = nil;
  1129.                 return 0;
  1130.             }
  1131.             delstrt = temp;
  1132.             return 1;
  1133.         }
  1134.  
  1135.         /*   -o   rev1-rev2   */
  1136.     if (!expandsym(delrev.end, &numrev)) return 0;
  1137.     if (
  1138.         length != countnumflds(numrev.string)
  1139.         ||    length>2 && compartial(numrev.string, target->num, length-1)
  1140.     ) {
  1141.         error("invalid revision range %s-%s", target->num, numrev.string);
  1142.             return 0;
  1143.         }
  1144.  
  1145.     target2 = genrevs(numrev.string,(char*)nil,(char*)nil,(char*)nil,&gendeltas);
  1146.         if ( ! target2 ) return 0;
  1147.  
  1148.         if ( length > 2) {  /* delete revisions on branches  */
  1149.             if ( cmpnum(target->num, target2->num) > 0) {
  1150.         if (cmpnum(target2->num, numrev.string))
  1151.                     flag = true;
  1152.                 else
  1153.                     flag = false;
  1154.                 temp = target;
  1155.                 target = target2;
  1156.                 target2 = temp;
  1157.             }
  1158.             if ( flag ) {
  1159.                 if ( ! cmpnum(target->num, target2->num) ) {
  1160.             error("Revisions %s-%s don't exist.", delrev.strt,delrev.end);
  1161.                     return 0;
  1162.                 }
  1163.                 cuthead = target;
  1164.                 temp = target->next;
  1165.             }
  1166.             else
  1167.                 temp = searchcutpt(target->num, length, gendeltas);
  1168.             cuttail = target2->next;
  1169.         }
  1170.         else { /*  delete revisions on trunk  */
  1171.             if ( cmpnum( target->num, target2->num) < 0 ) {
  1172.                 temp = target;
  1173.                 target = target2;
  1174.                 target2 = temp;
  1175.             }
  1176.             else
  1177.         if (cmpnum(target2->num, numrev.string))
  1178.                     flag = true;
  1179.                 else
  1180.                     flag = false;
  1181.             if ( flag ) {
  1182.                 if ( ! cmpnum(target->num, target2->num) ) {
  1183.             error("Revisions %s-%s don't exist.", delrev.strt, delrev.end);
  1184.                     return 0;
  1185.                 }
  1186.                 cuttail = target2;
  1187.             }
  1188.             else
  1189.                 cuttail = target2->next;
  1190.             temp = searchcutpt(target->num, length, gendeltas);
  1191.         }
  1192.         if ( branchpoint(temp, cuttail) )  {
  1193.             cuttail = nil;
  1194.             return 0;
  1195.         }
  1196.         delstrt = temp;
  1197.         return 1;
  1198. }
  1199.  
  1200.  
  1201.  
  1202.     static void
  1203. updateassoc()
  1204. /*   Function: add or delete(if revno is nil) association    */
  1205. /*        which is stored in assoclst            */
  1206.  
  1207. {
  1208.     const struct Symrev *curassoc;
  1209.     struct  assoc   * pre,  * pt;
  1210.  
  1211.         /*  add new associations   */
  1212.         curassoc = assoclst;
  1213.         while( curassoc ) {
  1214.             if ( curassoc->revno == nil ) {  /* delete symbol  */
  1215.         pre = pt = Symbols;
  1216.                 while( pt && strcmp(pt->symbol,curassoc->ssymbol) ) {
  1217.             pre = pt;
  1218.             pt = pt->nextassoc;
  1219.         }
  1220.         if ( pt )
  1221.             if ( pre == pt )
  1222.             Symbols = pt->nextassoc;
  1223.             else
  1224.             pre->nextassoc = pt->nextassoc;
  1225.         else
  1226.             warn("can't delete nonexisting symbol %s",curassoc->ssymbol);
  1227.         }
  1228.         else if (expandsym(curassoc->revno, &numrev)) {
  1229.         /*   add symbol  */
  1230.         VOID addsymbol(fstrsave(numrev.string), curassoc->ssymbol, curassoc->override);
  1231.             }
  1232.             curassoc = curassoc->nextsym;
  1233.         }
  1234.  
  1235. }
  1236.  
  1237.  
  1238.  
  1239.     static void
  1240. updatelocks()
  1241. /* Function: remove lock for caller or first lock if unlockcaller is set;
  1242.  *           remove locks which are stored in rmvlocklst,
  1243.  *           add new locks which are stored in newlocklst,
  1244.  *           add lock for Dbranch or Head if lockhead is set.
  1245.  */
  1246. {
  1247.     const struct Lockrev *lockpt;
  1248.     struct hshentry *target;
  1249.  
  1250.     if (unlockcaller) { /*  find lock for caller  */
  1251.             if ( Head ) {
  1252.         if (Locks) {
  1253.             switch (findlock(true, &target)) {
  1254.               case 0:
  1255.             breaklock(Locks->delta); /* remove most recent lock */
  1256.             break;
  1257.               case 1:
  1258.             diagnose("%s unlocked\n",target->num);
  1259.             break;
  1260.             }
  1261.         } else {
  1262.             warn("No locks are set.");
  1263.         }
  1264.             } else {
  1265.         warn("can't unlock an empty tree");
  1266.             }
  1267.         }
  1268.  
  1269.         /*  remove locks which are stored in rmvlocklst   */
  1270.         lockpt = rmvlocklst;
  1271.         while( lockpt ) {
  1272.         if (expandsym(lockpt->revno, &numrev)) {
  1273.         target = genrevs(numrev.string, (char *)nil, (char *)nil, (char *)nil, &gendeltas);
  1274.                 if ( target )
  1275.            if (!(countnumflds(numrev.string)&1) && cmpnum(target->num,numrev.string))
  1276.             error("can't unlock nonexisting revision %s",lockpt->revno);
  1277.                    else
  1278.             breaklock(target);
  1279.                         /* breaklock does its own diagnose */
  1280.             }
  1281.             lockpt = lockpt->nextrev;
  1282.         }
  1283.  
  1284.         /*  add new locks which stored in newlocklst  */
  1285.         lockpt = newlocklst;
  1286.         while( lockpt ) {
  1287.         setlock(lockpt->revno);
  1288.             lockpt = lockpt->nextrev;
  1289.         }
  1290.  
  1291.     if (lockhead) {  /*  lock default branch or head  */
  1292.             if (Dbranch) {
  1293.         setlock(Dbranch);
  1294.         } else if (Head) {
  1295.         if (0 <= addlock(Head))
  1296.             diagnose("%s locked\n",Head->num);
  1297.             } else {
  1298.         warn("can't lock an empty tree");
  1299.             }
  1300.         }
  1301.  
  1302. }
  1303.  
  1304.  
  1305.  
  1306.     static void
  1307. setlock(rev)
  1308.     const char *rev;
  1309. /* Function: Given a revision or branch number, finds the corresponding
  1310.  * delta and locks it for caller.
  1311.  */
  1312. {
  1313.         struct  hshentry *target;
  1314.  
  1315.     if (expandsym(rev, &numrev)) {
  1316.         target = genrevs(numrev.string, (char*)nil, (char*)nil,
  1317.                  (char*)nil, &gendeltas);
  1318.             if ( target )
  1319.            if (!(countnumflds(numrev.string)&1) && cmpnum(target->num,numrev.string))
  1320.             error("can't lock nonexisting revision %s", numrev.string);
  1321.                else
  1322.             if (0 <= addlock(target))
  1323.             diagnose("%s locked\n", target->num);
  1324.         }
  1325. }
  1326.  
  1327.  
  1328.  
  1329.     static void
  1330. rcs_setstate(rev,status)
  1331.     const char *rev, *status;
  1332. /* Function: Given a revision or branch number, finds the corresponding delta
  1333.  * and sets its state to status.
  1334.  */
  1335. {
  1336.         struct  hshentry *target;
  1337.  
  1338.     if (expandsym(rev, &numrev)) {
  1339.         target = genrevs(numrev.string, (char*)nil, (char*)nil,
  1340.                  (char*)nil, &gendeltas);
  1341.             if ( target )
  1342.            if (!(countnumflds(numrev.string)&1) && cmpnum(target->num,numrev.string))
  1343.             error("can't set state of nonexisting revision %s to %s",
  1344.               numrev.string, status);
  1345.                else
  1346.                     target->state = status;
  1347.         }
  1348. }
  1349.  
  1350.  
  1351.  
  1352.  
  1353.  
  1354.     static int
  1355. buildeltatext(deltas)
  1356.     const struct hshentries *deltas;
  1357. /*   Function:  put the delta text on frewrite and make necessary   */
  1358. /*              change to delta text                                */
  1359. {
  1360.     int exit_stats;
  1361.     register FILE *fcut;    /* temporary file to rebuild delta tree */
  1362.     const char *cutfilename, *diffilename;
  1363.  
  1364.     cutfilename = nil;
  1365.     cuttail->selector = false;
  1366.     inittmpeditfiles();
  1367.     scanlogtext(deltas->first, false);
  1368.         if ( cuthead )  {
  1369.         cutfilename = maketemp(3);
  1370.         errno = 0;
  1371.             if ( (fcut = fopen(cutfilename, "w")) == NULL) {
  1372.         efaterror(cutfilename);
  1373.             }
  1374.  
  1375.         while (deltas->first != cuthead) {
  1376.         deltas = deltas->rest;
  1377.         scanlogtext(deltas->first, true);
  1378.             }
  1379.  
  1380.         finishedit((struct hshentry *)nil);
  1381.         arewind(fcopy);
  1382.         fastcopy(fcopy, fcut);
  1383.             swapeditfiles(false);
  1384.             ffclose(fcut);
  1385.         }
  1386.  
  1387.     while (deltas->first != cuttail)
  1388.         scanlogtext((deltas = deltas->rest)->first, true);
  1389.         finishedit((struct hshentry *)nil);    ffclose(fcopy);
  1390.  
  1391.         if ( cuthead ) {
  1392.         diffilename = maketemp(0);
  1393.             exit_stats = run((char*)nil,diffilename,
  1394.             DIFF DIFF_FLAGS, cutfilename, resultfile, (char*)nil);
  1395.         if (!WIFEXITED(exit_stats) || 1<WEXITSTATUS(exit_stats))
  1396.                 faterror ("diff failed");
  1397.         return putdtext(cuttail->num,curlogmsg,diffilename,frewrite,true);
  1398.     } else
  1399.         return putdtext(cuttail->num,curlogmsg,resultfile,frewrite,false);
  1400. }
  1401.  
  1402.  
  1403.  
  1404.     static void
  1405. buildtree()
  1406. /*   Function:  actually removes revisions whose selector field  */
  1407. /*        is false, and rebuilds the linkage of deltas.     */
  1408. /*              asks for reconfirmation if deleting last revision*/
  1409. {
  1410.     struct    hshentry   * Delta;
  1411.         struct  branchhead      *pt, *pre;
  1412.  
  1413.         if ( cuthead )
  1414.            if ( cuthead->next == delstrt )
  1415.                 cuthead->next = cuttail;
  1416.            else {
  1417.                 pre = pt = cuthead->branches;
  1418.                 while( pt && pt->hsh != delstrt )  {
  1419.                     pre = pt;
  1420.                     pt = pt->nextbranch;
  1421.                 }
  1422.                 if ( cuttail )
  1423.                     pt->hsh = cuttail;
  1424.                 else if ( pt == pre )
  1425.                     cuthead->branches = pt->nextbranch;
  1426.                 else
  1427.                     pre->nextbranch = pt->nextbranch;
  1428.             }
  1429.     else {
  1430.             if ( cuttail == nil && !quietflag) {
  1431.         if (!yesorno(false, "Do you really want to delete all revisions? [ny](n): ")) {
  1432.             error("No revision deleted");
  1433.             Delta = delstrt;
  1434.             while( Delta) {
  1435.             Delta->selector = true;
  1436.             Delta = Delta->next;
  1437.             }
  1438.             return;
  1439.         }
  1440.         }
  1441.             Head = cuttail;
  1442.     }
  1443.         return;
  1444. }
  1445.  
  1446. #if lint
  1447. /* This lets us lint everything all at once. */
  1448.  
  1449. const char cmdid[] = "";
  1450.  
  1451. #define go(p,e) {int p P((int,char**)); void e P((void)); if(*argv)return p(argc,argv);if(*argv[1])e();return 0;}
  1452.  
  1453.     int
  1454. main(argc, argv)
  1455.     int argc;
  1456.     char **argv;
  1457. {
  1458.     switch (argc) {
  1459.         case 0:    go(ciId,    ciExit);
  1460.         case 1:    go(coId,    coExit);
  1461.         case 2:    go(identId,    identExit);
  1462.         case 3:    go(rcsdiffId,    rdiffExit);
  1463.         case 4:    go(rcsmergeId,    rmergeExit);
  1464.         case 5:    go(rlogId,    rlogExit);
  1465.         case 6:    go(rcsId,    exiterr);
  1466.         default: return 0;
  1467.     }
  1468. }
  1469. #endif
  1470.